struct segdesc {
	uint lim_15_0 :16;  // Low bits of segment limit
	uint base_15_0 :16; // Low bits of segment base address
	uint base_23_16 :8; // Middle bits of segment base address
	uint type :4;       // Segment type (see STS_ constants)
	uint s :1;          // 0 = system, 1 = application
	uint dpl :2;        // Descriptor Privilege Level
	uint p :1;          // Present
	uint lim_19_16 :4;  // High bits of segment limit
	uint avl :1;        // Unused (available for software use)
	uint rsv1 :1;       // Reserved
	uint db :1;         // 0 = 16-bit segment, 1 = 32-bit segment
	uint g :1;          // Granularity: limit scaled by 4K when set
	uint base_31_24 :8; // High bits of segment base address
};

struct gatedesc {
	uint off_15_0 :16;   // low 16 bits of offset in segment
	uint cs :16;         // code segment selector
	uint args :5;        // # args, 0 for interrupt/trap gates
	uint rsv1 :3;        // reserved(should be zero I guess)
	uint type :4;        // type(STS_{TG,IG32,TG32})
	uint s :1;           // must be 0 (system)
	uint dpl :2;         // descriptor(meaning new) privilege level
	uint p :1;           // Present
	uint off_31_16 :16;  // high bits of offset in segment
};

#define SETGATE(gate, istrap, sel, off, d)                \
{                                                         \
  (gate).off_15_0 = (uint)(off) & 0xffff;                \
  (gate).cs = (sel);                                      \
  (gate).args = 0;                                        \
  (gate).rsv1 = 0;                                        \
  (gate).type = (istrap) ? STS_TG32 : STS_IG32;           \
  (gate).s = 0;                                           \
  (gate).dpl = (d);                                       \
  (gate).p = 1;                                           \
  (gate).off_31_16 = (uint)(off) >> 16;                  \
}

struct trapframe {
	// registers as pushed by pusha
	uint edi;
	uint esi;
	uint ebp;
	uint oesp;      // useless & ignored
	uint ebx;
	uint edx;
	uint ecx;
	uint eax;

	// rest of trap frame
	ushort gs;
	ushort padding1;
	ushort fs;
	ushort padding2;
	ushort es;
	ushort padding3;
	ushort ds;
	ushort padding4;
	uint trapno;

	// below here defined by x86 hardware
	uint err;
	uint eip;
	ushort cs;
	ushort padding5;
	uint eflags;

	// below here only when crossing rings, such as from user to kernel
	uint esp;
	ushort ss;
	ushort padding6;
};

static inline uchar inb(ushort port) {
	uchar data;

	asm volatile("in %1,%0" : "=a" (data) : "d" (port));
	return data;
}

static inline void insl(int port, void *addr, int cnt) {
	asm volatile("cld; rep insl" :
			"=D" (addr), "=c" (cnt) :
			"d" (port), "0" (addr), "1" (cnt) :
			"memory", "cc");
}

static inline void outb(ushort port, uchar data) {
	asm volatile("out %0,%1" : : "a" (data), "d" (port));
}

static inline void stosb(void *addr, int data, int cnt) {
	asm volatile("cld; rep stosb" :
			"=D" (addr), "=c" (cnt) :
			"0" (addr), "1" (cnt), "a" (data) :
			"memory", "cc");
}

static inline void stosl(void *addr, int data, int cnt) {
	asm volatile("cld; rep stosl" :
			"=D" (addr), "=c" (cnt) :
			"0" (addr), "1" (cnt), "a" (data) :
			"memory", "cc");
}

static inline void cli(void) {
	asm volatile("cli");
}

static inline void sti(void) {
	asm volatile("sti");
}

static inline void lcr3(uint val) {
	asm volatile("movl %0,%%cr3" : : "r" (val));
}

static inline void ltr(ushort sel) {
	asm volatile("ltr %0" : : "r" (sel));
}

static inline void lidt(struct gatedesc *p, int size) {
	volatile ushort pd[3];

	pd[0] = size - 1;
	pd[1] = (uint) p;
	pd[2] = (uint) p >> 16;

	asm volatile("lidt (%0)" : : "r" (pd));
}

static inline void loadgs(ushort v) {
	asm volatile("movw %0, %%gs" : : "r" (v));
}

static inline void lgdt(struct segdesc *p, int size) {
	volatile ushort pd[3];

	pd[0] = size - 1;
	pd[1] = (uint) p;
	pd[2] = (uint) p >> 16;

	asm volatile("lgdt (%0)" : : "r" (pd));
}

static inline uint xchg(volatile uint *addr, uint newval) {
	uint result;

	// The + in "+m" denotes a read-modify-write operand.
	asm volatile("lock; xchgl %0, %1" :
			"+m" (*addr), "=a" (result) :
			"1" (newval) :
			"cc");
	return result;
}

static inline uint readeflags(void) {
	uint eflags;
	asm volatile("pushfl; popl %0" : "=r" (eflags));
	return eflags;
}
